#include "ElementDOM.h"

#include "ATL.h"

//#include "debugstreams.h"

#include <algorithm>

#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost::assign;

const string ElementDOM::null("");

bool ElementDOM::niveauxLus( false );

const vector< string > ElementDOM::NomsNiveaux =
  list_of( "ElementDOM::interne" );

DebugLevelType ElementDOM::interne( BLANK );

/**
 * Constructeur par défaut, permet également de déterminer tous les
 * champs sauf les attributs de l'ElementDOM.
 *
 * @param[in] bal nom de la balise (tag).
 * @param[in] niv est le niveau dans l'arbre DOM.
 * @param[in] data est la valeur du champ de données.
 * @param[in] numero est le numéro de l'ElementDOM au sein du document
 * lu.
 * @param[in] parent numéro du parent.
 * @param[in] aDonnees \e true si la balise contient des données,
 * false sinon.
 */
ElementDOM::ElementDOM(const string& bal, const unsigned& niv,
		       const string& data, const unsigned& numero,
		       const ElementDOM* parent, const bool& aDonnees)
  : ADDObjetBase( "ElementDOM" ), Balise( bal ), Niveau( niv ),
    Donnees( data ), Numero( numero ), Parent( parent ),
    contientDonnees( aDonnees ), nbrAttributs( 0 )
{
  if ( ! niveauxLus )
    {
      lireNiveaux( this, NomsNiveaux );
      niveauxLus = true;
    }
}

/**
 * Construteur de copie, effectue un copie profonde.
 */
ElementDOM::ElementDOM(const ElementDOM& dom)
  : ADDObjetBase( "ElementDOM" ), Balise( dom.balise() ),
    Niveau( dom.niveau() ), Donnees( dom.donnees() ),
    Numero( dom.numero() ), Parent( dom.parent() ),
    contientDonnees( dom.aDonnees() ), nbrAttributs( dom.nbrAttributs )
    
{
  if ( ! niveauxLus )
    {
      lireNiveaux( this, NomsNiveaux );
      niveauxLus = true;
    }

  Attributs = dom.Attributs;
}

/**
 * Aucune allocation dynamique effectuée dans cette classe, rie à
 * faire ici.
 */
ElementDOM::~ElementDOM()
{}

/**
 * Permet de fixer le nom de la balise
 *
 * @param[in] bal nom de la balise
 */
void ElementDOM::setBalise(const string& bal)
{
  Balise = bal;
}

/**
 * Permet de fixer le niveau de l'ElementDOM
 *
 * @param[in] lvl niveau de l'ElementDOM
 */
void ElementDOM::setNiveau(const unsigned& lvl)
{
  Niveau = lvl;
}

/**
 * Permet de fixer la valeur du champ de données
 *
 * @param[in] data valeur du champ de données
 */
void ElementDOM::setDonnees(const string& data)
{
  Donnees = data;
}

/**
 * Permet de fixer le numéro
 *
 * @param[in] numero numéro de l'ElementDOM
 */
void ElementDOM::setNumero(const unsigned& numero)
{
  Numero = numero;
}

/**
 * Permet de fixer le parent
 *
 * @param[in] parent parent de l'ElementDOM.
 */
void ElementDOM::setParent(const ElementDOM*& parent)
{
  Parent = parent;
}

/**
 * Permet d'ajouter un enfant à la liste.
 *
 * Une fois l'ajout effectué, on contrôle que chaque enfant n'est
 * présent qu'une fois.
 *
 * @param[in] enfant enfant à ajouter.
 */
void ElementDOM::ajoutEnfant(/*const*/ ElementDOM*& enfant)
{
  Enfants.push_back( enfant );

  // vector< const ElementDOM* >::iterator it;

  // sort( Enfants.begin(), Enfants.end() );
  // it = unique( Enfants.begin(), Enfants.end() );

  // Enfants.erase( it, Enfants.end() );
}

/**
 * Permet de changer la valeur du booléen qui gère la présence de
 * données
 *
 * @param[in] aDonnees \e true si oui, \e false dans le cas contraire.
 */
void ElementDOM::setADonnees(const bool& aDonnees)
{
  contientDonnees = aDonnees;
}

/**
 * Permet d'ajouter un attribut ainsi que sa valeur associée à
 * l'ElementDOM.\n
 *
 * Les niveaux de sorties suivant permettent d'afficher les informations :\n
 * FUNC début et fin de méthode\n
 * DEBUG récapitulation de l'élément ajouté
 *
 * @param[in] attrib nom de l'attribut
 * @param[in] val valeur de cet attribut
 */
void ElementDOM::ajoutAttribut(const string& attrib, const string& val)
{
  /*Debug::*/empileNiveau( &interne );
  //DebugLevelType etat_precedent( dout.getLevel() );
  //dout.setLevel( interne );
  func() << "ElementDOM::ajoutAttribut -> début" << endmsg;

  if ( attrib[0] == ' ' || attrib.empty() || attrib[0] == 0 )
    {
      func() << "ElementDOM::ajoutAttribut -> fin" << endmsg;
      /*Debug::*/depileNiveau();//dout.setLevel( etat_precedent );
      return;
    }
  else if ( Attributs.find( attrib ) == Attributs.end() )
    {
      debug() << "Ajout attribut " << attrib << ", valeur " << val << endmsg;
      nbrAttributs++;
      Attributs[ attrib ] = val;
    }

  func() << "ElementDOM::ajoutAttribut -> fin" << endmsg;
  /*Debug::*/depileNiveau();//dout.setLevel( etat_precedent );
}

/**
 * Permet de récupérer le nom de la balise
 *
 * @return nom de la balise (tag)
 */
const string& ElementDOM::balise() const
{
  return Balise;
}

/**
 * Permet de récupérer le niveau
 *
 * @return niveau
 */
const unsigned& ElementDOM::niveau() const
{
  return Niveau;
}

/**
 * Permet de récupérer la valeur du champ de données
 *
 * @return champ de données
 */
const string& ElementDOM::donnees() const
{
  return Donnees;
}

/**
 * Permet de récupérer le numéro
 *
 * @return numero
 */
const unsigned& ElementDOM::numero() const
{
  return Numero;
}

/**
 * Permet de récupérer le parent
 *
 * @return parent
 */
const ElementDOM* ElementDOM::parent() const
{
  return Parent;
}

/**
 * Permet de récupérer la liste des enfants
 *
 * @return pointeur sur le conteneur des parents.
 */
const vector< const ElementDOM* >* ElementDOM::enfants() const
{
  return &Enfants;
}

/**
 * Permet de savoir si le champ contien des données ou pas
 *
 * @return \e true si contient des données, \e false sinon
 */
const bool& ElementDOM::aDonnees() const
{
  return contientDonnees;
}

/**
 * Permet de savoir si l'ElementDOM a des attributs
 *
 * @return \e true si oui, \e false sinon
 */
bool ElementDOM::aAttributs() const
{
  return ( Attributs.begin() != Attributs.end() );
}

/**
 * Permet de récupérer le nom de tous les attibuts de l'ElementDOM
 *
 * @return vecteur contenant les noms des attributs
 *
 * @return niveau
 */
vector< string > ElementDOM::attributs() const
{
  // map< string, string >::const_iterator It, Begin( Attributs.begin() ),
  //   End( Attributs.end() );

  vector< string > retour;

  // for (It = Begin; It != End; It++)
  //   retour . push_back( It -> first );

  ExtraireClef< string, string > extraitStr;

  retour.resize( Attributs.size() );

  transform( Attributs.begin(), Attributs.end(), retour.begin(),
	     extraitStr );

  return retour;
}

/**
 * Permet de récupérer la valeur d'un attibut
 *
 * @param[in] attrib nom de l'attribut dont on veut conaître la valeur
 *
 * @return valeur de cet attribut
 */
const string& ElementDOM::attribut(const string& attrib) const
{
  if ( Attributs.find( attrib ) == Attributs.end() )
    return null;
  else
    return Attributs.find( attrib ) -> second;
}


/**
 * Permet de récupérer la valeur d'un attribut
 *
 * @param[in] num numéro de l'attribut désiré
 *
 * @return valeur de cet attribut
 */
const string& ElementDOM::attribut(const unsigned& num) const
{
  map< string, string >::const_iterator It, Begin( Attributs.begin() ),
    End( Attributs.end() );

  unsigned i(0);

  for (It = Begin; It != End; It++, i++)
    {
      if ( i == num )
	return It -> second;
    }
  
  return null;
}

/**
 * Opérateur d'affectation, effectue la copie profonde.
 *
 * @param[in] dom ElementDOM à copier
 *
 * @return référence sur l'instance courante, pour pouvoir effecturer
 * A = b = c.
 */
ElementDOM& ElementDOM::operator=(const ElementDOM& dom)
{
  if ( this != &dom )
    {
      Balise          = dom.balise();
      Niveau          = dom.niveau();
      Donnees         = dom.donnees();
      Numero          = dom.numero();
      Attributs       = dom.Attributs;
      Parent          = dom.parent();
      contientDonnees = dom.aDonnees();
      nbrAttributs    = dom.nbrAttributs;
    }
  return *this;
}

/**
 * Permet d'afficher le nom de l'élément, sa valeur ainsi que ses
 * éventuels asstributs.
 *
 * @param[in] os flot de sortie
 *
 * @return référence sur le flot utilisé, pour pouvoir réutiliser le
 * flot sans problème
*/
ostream& ElementDOM::afficheAttributs(ostream& os) const
{
  map< string, string >::const_iterator It, Begin( Attributs.begin() ),
    End( Attributs.end() );

  unsigned i(0);

  for (It = Begin; It != End; It++, i++)
    {
      os << It -> first << " = " << It -> second;
      if ( i != nbrAttributs - 1 )
	os << ", ";
    }
  return os;
}

/**
 * Permet d'afficher le nom de l'élément, sa valeur ainsi que ses
 * éventuels asstributs.
 *
 * @param[in] os flot de sortie
 *
 * @return référence sur le flot utilisé, pour pouvoir réutiliser le
 * flot sans problème
*/
FlotMessage& ElementDOM::afficheAttributs(FlotMessage& flot) const
{
  map< string, string >::const_iterator It, Begin( Attributs.begin() ),
    End( Attributs.end() );

  unsigned i(0);

  for (It = Begin; It != End; It++, i++)
    {
      flot << It -> first << " = " << It -> second;
      if ( i != nbrAttributs - 1 )
	flot << ", ";
    }
  return flot;
}

/**
 * Permet de régler le niveau de sortie de la classe.
 *
 * @param[in] lvl niveau de sortie
 */
void ElementDOM::setLevel(const unsigned& lvl, const unsigned&)
{
  interne = static_cast< DebugLevelType >( lvl );
}
